using Castle.Core.Internal;
using Castle.DynamicProxy;
using Microsoft.AspNetCore.Mvc.TagHelpers.Cache;
using Microsoft.IdentityModel.Logging;
using Newtonsoft.Json;
using Serilog;
using Serilog.Core;
using System.Diagnostics;
using System.Reflection;
using xjjxmm.infrastructure.ToolKit;
using xjjxmm.infrastructure.ToolKit.IdKit;
using XjjXmm.Infrastructure.ToolKit;
using XjjXmm.Infrastructure.User;

namespace xjjxmm.infrastructure.xjjxmmLog.AuditLog;

public class AuditLoggerInterceptor : IInterceptor
{
    private readonly AuditLoggerAsyncInterceptor _interceptor;

    public AuditLoggerInterceptor(AuditLoggerAsyncInterceptor interceptor)
    {
        _interceptor = interceptor;
    }

    public void Intercept(IInvocation invocation)
    {
        _interceptor.ToInterceptor().Intercept(invocation);
    }

}

public class AuditLoggerAsyncInterceptor : IAsyncInterceptor
{
    private readonly UserContext userContext;

    public AuditLoggerAsyncInterceptor(IUserContextAccessor userContextAccessor)
    {
        this.userContext = userContextAccessor.UserContext;
    }

    private AuditLog? buildAuditLog(IInvocation invocation)
    {
        //记录日志
        var logAttribute = invocation.Method.GetCustomAttribute<AuditLogAttribute>();

        if (logAttribute == null || logAttribute.Action.IsNullOrEmpty())
        {
            return null;
        }

        //获取执行信息
        var methodName = invocation.Method.Name;
        var registId = invocation.Arguments
            .Select(t => t.GetValueHaveCustomAttribute<AuditLogIdAttribute>())
            .FirstOrDefault(t => t != null);

        var auditLog = new AuditLog
        {
            Id = SnowFlakeKit.NextIdStr(),
            ActionId = logAttribute.Action,
            RegistId = registId?.ToString(),
            UserId = userContext.Id,
            UserName = userContext.NickName,
            //TentantId 
            // TentantName 
            RequestData = invocation.Arguments,
            RequestTime = DateTime.Now
        };

        return auditLog;
    }

    private void saveAuditLog(AuditLog? auditLog, object? result)
    {
        if (auditLog != null)
        {
            auditLog.ResponseData = result;
            auditLog.ResponseTime = DateTime.Now;
        }
    }
    //
    // 摘要:
    //     Intercepts a synchronous method invocation.
    //
    // 参数:
    //   invocation:
    //     The method invocation.
    public void InterceptSynchronous(IInvocation invocation)
    {
        var logger = Log.ForContext(invocation.TargetType);

        var auditLog = buildAuditLog(invocation);

        invocation.Proceed();
        var task = invocation.ReturnValue;
        invocation.ReturnValue = task;


        saveAuditLog(auditLog, task);
    }

    //
    // 摘要:
    //     Intercepts an asynchronous method invocation with return type of System.Threading.Tasks.Task.
    //
    // 参数:
    //   invocation:
    //     The method invocation.
    public void InterceptAsynchronous(IInvocation invocation)
    {
        invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
    }

    //
    // 摘要:
    //     Intercepts an asynchronous method invocation with return type of System.Threading.Tasks.Task`1.
    //
    // 参数:
    //   invocation:
    //     The method invocation.
    //
    // 类型参数:
    //   TResult:
    //     The type of the System.Threading.Tasks.Task`1 System.Threading.Tasks.Task`1.Result.

    public void InterceptAsynchronous<TResult>(IInvocation invocation)
    {
        //调用业务方法
        invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
    }

    private async Task InternalInterceptAsynchronous(IInvocation invocation)
    {
        var logger = Log.ForContext(invocation.TargetType);

        var auditLog = buildAuditLog(invocation);

        //执行方法
        invocation.Proceed();
        var task = (Task)invocation.ReturnValue;
        await task;


        saveAuditLog(auditLog, null);

    }

    private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
    {

        var logger = Log.ForContext(invocation.TargetType);

        var auditLog = buildAuditLog(invocation);


        invocation.Proceed();
        var task = (Task<TResult>)invocation.ReturnValue;
        TResult result = await task;


        saveAuditLog(auditLog, result);

        return result;
    }
}